package pers.cjr.demo.redis;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.platform.commons.logging.LoggerFactory;
import org.redisson.Redisson;
import org.redisson.RedissonLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.event.annotation.AfterTestClass;
import org.springframework.test.context.event.annotation.BeforeTestClass;
import pers.cjr.DemoApplication;

/**
 * RedissonLock Demo : 分布式非公平可重入互斥锁
 *
 * @author Aaron Zhu
 * @date 2022-04-04
 */
@Slf4j
@SpringBootTest(classes = DemoApplication.class)
public class RedissonLockDemo {

    private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");

    private static ExecutorService threadPool = Executors.newFixedThreadPool(2);

    private static RedissonClient redissonClient;

    @BeforeEach
    public void init() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://127.0.0.1:6379")
//                .setPassword("123456")
        ;
        redissonClient = Redisson.create(config);
    }

    /**
     * 测试: 阻塞式获取锁、可重入性
     */
    @Test
    public void testLock1() throws InterruptedException {
        final String lockName = "sellPc";
        RLock tempLock = redissonClient.getLock(lockName);
        if (tempLock instanceof RedissonLock) {
            System.out.println("锁类型: RedissonLock");
        }
        //计数器
        CountDownLatch countDownLatch = new CountDownLatch(2);

        threadPool.execute(() -> {
            info("进入");
            RLock lock = redissonClient.getLock(lockName);
            try {
                // 阻塞式获取锁
                lock.lock(4, TimeUnit.SECONDS);
                info("成功获取锁");
                // 模拟业务耗时
                info("执行业务20秒");
                Thread.sleep(20000);
            } catch (Exception e) {
                System.out.println("Happen Exception: " + e.getMessage());
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    info("释放锁");
                    lock.unlock();
                } else {
                    info("当前线程未使用锁！（或已经提前关闭）");
                }
                countDownLatch.countDown();
                info("结束");
            }
        });

        Thread.sleep(1000);
        threadPool.execute(() -> {
            // 设置分布式锁
            info("进入");
            RLock lock = redissonClient.getLock(lockName);
            try {
                lock.lock();
                info("执行业务10秒");
                Thread.sleep(10000);
                //x秒内如果没有拿到同步锁，返回false
//                if (lock.tryLock(2, TimeUnit.SECONDS)) {
//                    lock.lock();
//                    info("执行业务10秒");
//                    Thread.sleep(10000);
//                } else {
//                    info("等待了许久，获取不到锁！");
//                    lock.unlock();
//                }
            } catch (Exception e) {
                System.out.println("Happen Exception: " + e.getMessage());
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    info("释放锁");
                    lock.unlock();
                } else {
                    info("当前线程未使用锁！（或已经提前关闭）");
                }
                countDownLatch.countDown();
                info("结束");
            }
        });

        // 主线程等待所有任务执行完毕
        countDownLatch.await(3 * 60, TimeUnit.SECONDS);

        System.out.println(
                formatter.format(LocalTime.now()) + "---------------------- 系统下线 ----------------------");
    }


    @AfterEach
    public void close() {
        redissonClient.shutdown();
    }

    /**
     * 打印信息
     *
     * @param msg
     */
    private static void info(String msg) {
        String time = formatter.format(LocalTime.now());
        String thread =
                "线程：" + Thread.currentThread().getId();
        String log = "[" + time + "] " + " <" + thread + "> " + msg;
        System.out.println(log);
    }
} 